{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b09a4223",
   "metadata": {},
   "source": [
    "# 单量子比特标定\n",
    "\n",
    "\n",
    "*版权所有 (c) 2021 百度量子计算研究所，保留所有权利。*"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "23bde5df",
   "metadata": {},
   "source": [
    "## 内容概要\n",
    "\n",
    "本教程介绍单量子比特频率、弛豫时间 $T_1$ 和失相时间 $T_2$ 的标定方法以及该量子比特上 $\\pi$ 脉冲的校准。本教程的大纲如下：\n",
    "- 背景介绍\n",
    "- 准备工作\n",
    "- 构建模拟器\n",
    "- 量子比特频率标定\n",
    "- Rabi 振荡校准 $\\pi$ 脉冲\n",
    "- 纵向弛豫标定 $T_1$\n",
    "- Ramsey 振荡标定 $T_2$\n",
    "- 总结"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d35ceb82",
   "metadata": {},
   "source": [
    "## 背景介绍\n",
    "\n",
    "由于制造工艺的限制以及实际应用的需要，不同的超导量子比特具有不同的频率、相干时间等特性。因此我们需要对这些参数进行标定，即对量子比特执行一系列操作，并进行测量，从测量结果中获取关于此量子比特的信息，如量子比特频率以及相干时间 $T_1$、$T_2$ 等。其中，量子比特的频率为实现单量子比特门的脉冲信号的驱动频率；相干时间为量子比特保持其信息的持续时间，相干时间越长，量子比特的质量越好，可进行运算的时间就越长。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "beb49325",
   "metadata": {},
   "source": [
    "## 准备工作\n",
    "\n",
    "在运行此教程前，您首先需要从量脉（Quanlse）和其他常用 Python 库导入必要的包。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fb38ffba",
   "metadata": {},
   "outputs": [],
   "source": [
    "from Quanlse.Superconduct.Simulator import pulseSim1Q\n",
    "from Quanlse.Superconduct.Calibration.SingleQubit import qubitSpec, ampRabi, fitRabi, longRelax, ramsey, fitRamsey\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "plt.rcParams[\"figure.figsize\"] = (7, 5)\n",
    "\n",
    "from numpy import array, pi, exp\n",
    "from scipy.signal import find_peaks"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74fb1a8f",
   "metadata": {},
   "source": [
    "## 构建模拟器\n",
    "\n",
    "在进行标定演示前，我们首先需要构建一个单量子比特模拟器作为标定对象。在 Quanlse v2.1 中我们内置了设定好参数的单量子比特模拟器 `pulseSim1Q()` （相关参数如量子比特频率以及 $T_1$ 和 $T_2$ 等可进行自定义）。`pulseSim1Q()` 函数需要两个参数：`dt` 表示求解模拟演化时的步长，而 `frameMode` 则表示采用何种坐标系进行仿真（`'lab'`、`'rot'` 分别表示实验室坐标系和旋转坐标系）。完成初始化后，我们将该模拟器视为 \"黑箱\" 进行标定的演示。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "21a239e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "# AWG sampling time\n",
    "dt = 0.01\n",
    "\n",
    "# Instantiate the simulator object\n",
    "model = pulseSim1Q(dt=dt, frameMode='lab')\n",
    "\n",
    "# Define system parameters\n",
    "model.qubitFreq = {0: 5.212 * (2 * pi)}\n",
    "model.T1 = {0: 2000}\n",
    "model.T2 = {0: 600}"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4da17bff",
   "metadata": {},
   "source": [
    "## 量子比特频率标定\n",
    "\n",
    "在标定量子比特的其它参数之前，我们首先需要确定量子比特的频率。量子比特频率确定后，我们就可以正确地设置本机振荡器（Local Oscillator）频率，从而使得施加的脉冲与量子比特共振。\n",
    "\n",
    "为了测量量子比特频率，我们利用外加脉冲与量子比特共振激发的原理，改变本机振荡器频率，对量子比特施加一定振幅的脉冲。量子比特被最大程度的激发时的脉冲频率，即为量子比特的频率。而在实际的实验中，量子比特频率的大致范围会提供给实验人员。因此，我们可以在给定的范围内进行频率扫描，并确定较为精确的量子比特频率。\n",
    "\n",
    "我们首先在较大的频率范围内（4.6 GHz 到 5.8 GHz）进行扫描。具体的方法为使用校准模块 `Quanlse.Calibration.SingleQubit` 中的函数 `qubitSpec()`，并输入脉冲模型 `pulseModel`、频率范围 `frequeRange`、样本数量 `sample`、脉冲幅度 `amp` 和脉冲持续时间 `t` 。在完成扫描后，该函数将返回扫描频率和对应的激发态布居数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23c5d5d1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define frequency range\n",
    "freqRange = [4.1 * (2 * pi), 5.9 * (2 * pi)]\n",
    "\n",
    "# Scan qubit frequency spectrum\n",
    "freqList, popList = qubitSpec(pulseModel=model, freqRange=freqRange, sample=50, amp=0.9, t=20)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5319e5a3",
   "metadata": {},
   "source": [
    "激发态布居数与本机振荡器频率关系图如下。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dbee057b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Convert unit\n",
    "freq = [x / (2 * pi) for x in freqList]\n",
    "\n",
    "# Plot population graph\n",
    "plt.plot(freq, popList)\n",
    "plt.title(\"Frequency spectrum\", size=17)\n",
    "plt.xlabel(\"LO frequency (GHz)\", size=15)\n",
    "plt.ylabel(r\"$|1\\rangle$ population)\", size=15)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ce39d5c",
   "metadata": {},
   "source": [
    "从图中我们可以看到量子比特频率大致在 5.1 GHz 和 5.3 GHz 之间。接下来我们缩小扫描范围进行第二次扫描，并绘制激发态布居数与本机振荡器频率关系图。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fc57b6bb",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define new frequency range\n",
    "nFreqRange = [5.1 * (2 * pi), 5.3 * (2 * pi)]\n",
    "\n",
    "# Scan qubit frequency spectrum\n",
    "nFreqList, nPopList = qubitSpec(model, nFreqRange, 30, 0.9, 20)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "63483831",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Convert unit\n",
    "nFreq = [x / (2 * pi) for x in nFreqList]\n",
    "\n",
    "# Plot population graph\n",
    "plt.plot(nFreq, nPopList)\n",
    "plt.title(\"Frequency spectrum\", size=17)\n",
    "plt.xlabel(\"LO frequency (GHz)\", size=15)\n",
    "plt.ylabel(r\"$|1\\rangle$ population)\", size=15)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e20edb6a",
   "metadata": {},
   "source": [
    "然后，我们使用 `scipy` 中的函数 `find_peak()` 来寻找峰值所对应的频率。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e47ed137",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Find peak\n",
    "peak = find_peaks(nPopList, height=0.3)[0][0]\n",
    "qubitFreq = nFreq[peak]\n",
    "\n",
    "# Plot peak\n",
    "plt.plot(nFreq, nPopList)\n",
    "plt.title(f'Qubit frequency: {round(qubitFreq, 6)} GHz', size=17)\n",
    "plt.plot(nFreq[peak], nPopList[peak], 'x', mfc=None, mec='red', mew=2, ms=8)\n",
    "plt.xlabel('Frequency (GHz)', size=15)\n",
    "plt.ylabel(r'$|1\\rangle$ population', size=15)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2034f61",
   "metadata": {},
   "source": [
    "如上图所示，我们标定得到的量子比特频率为 5.217241 GHz。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "439d3c0c",
   "metadata": {},
   "source": [
    "## Rabi 振荡校准 $\\pi$ 脉冲\n",
    "\n",
    "在确定了量子比特的频率后，我们可以校准 $\\pi$ 和 $\\pi/2$ 脉冲的波形参数。为此，我们进行 Rabi 振荡实验。通常有两种方式进行 Rabi 振荡：确定其他参数不变，固定脉冲振幅扫描脉冲持续时间或固定脉冲持续时间扫描脉冲振幅。选择适当的范围后，激发态（或基态）的布居数将以正弦波的形式振荡。为进行上述实验，我们从 `Quanlse.Calibration.SingleQubit` 模块导入函数 `ampRabi()`，并输入参数：脉冲模型 `pulseModel`、振幅范围 `ampRange`、脉冲持续时间 `tg` 和样本数量 `sample` 。该函数将返回扫描振幅和相应的激发态布居数列表。\n",
    "\n",
    "另外，`calibration` 模块还包括了通过扫描脉冲的时间的函数 `tRabi()`。该函数通过固定脉冲幅值并且改变脉冲的时间来实现 Rabi 振荡，因此用法与 `ampRabi()` 非常类似。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "86361469",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Define amplitude range\n",
    "ampRange = [0, 6]\n",
    "\n",
    "# Scan different amplitudes for Rabi oscillation\n",
    "ampList, popList = ampRabi(pulseModel=model, pulseFreq=qubitFreq * 2 * pi, \n",
    "                           ampRange=ampRange, tg=20, sample=50)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7ad8d198",
   "metadata": {},
   "source": [
    "激发布居数与脉冲振幅关系图如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6ba46e1d",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot Rabi Oscillation with different amplitudes\n",
    "plt.plot(ampList, popList, '.')\n",
    "plt.title(\"Rabi Oscillation\", size=17)\n",
    "plt.xlabel('Amplitude', size=15)\n",
    "plt.ylabel(r'$|1\\rangle$ population', size=15)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0a9955b",
   "metadata": {},
   "source": [
    "在得到布居数的分布之后，我们从 `Quanlse.Calibration.SingleQubit` 模块导入函数 `fitRabi()` 进行图像拟合，并获得能够实现 $\\pi$ 和 $\\pi/2$ 旋转的脉冲振幅。我们输入 `ampList` 作为 X 轴，并同时输入布居数 `popList` 作为 Y 轴进行拟合，其中拟合函数的形式为：$y=a\\cdot \\cos（b\\cdot x+c）+d$。最终，`fitRabi()` 将返回 $\\pi/2$ 和 $\\pi$ 脉冲的振幅："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ec889269",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fit Rabi\n",
    "halfPiAmp, piAmp = fitRabi(popList=popList, xList=ampList)\n",
    "print(\"Pi/2-pulse amplitude: \", halfPiAmp)\n",
    "print(\"Pi-pulse amplitude: \", piAmp)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ada03187",
   "metadata": {},
   "source": [
    "## 纵向弛豫标定 $T_1$\n",
    "\n",
    "得到 $\\pi$ 和 $\\pi/2$ 脉冲的参数后，我们可以进一步标定量子比特的相干时间 $T_1$ 和 $T_2$。我们首先进行 $T_1$ 的标定，将 $\\pi$ 脉冲施加到量子比特上，并找到激发态布居数衰减到 $1/e$ 的时间 \\[1\\]。\n",
    "\n",
    "为了将量子比特激发到激发态并观察其纵向弛豫，我们可以使用 `Quanlse.Calibration.SingleQubit` 模块中的 `longRelax()` 函数。输入参数：模拟器对象 `pulseModel`、AWG 采样时间 `dt`、脉冲频率 `pulseModel`、$\\pi$ 脉冲幅度 `piAmp` 和持续时间 `piLen`、最大闲置时间 `maxIdle` 和拟合函数的初始值 `initFit`。随后，运行该函数进行模拟仿真，同时该函数将使用拟合函数 $y=e^{-x/T_1}$ 进行曲线拟合。最终返回 $T_1$、闲置时间、布居数仿真结果以及拟合结果的列表："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f8f7bf1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Longitudinal relaxation on a qubit\n",
    "T1, tList, experimental, fitted = longRelax(pulseModel=model, dt=dt, pulseFreq=qubitFreq * 2 * pi, \n",
    "                                            piAmp=piAmp, piLen=20, maxIdle=4000, initFit=[1500])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c0c7bd6f",
   "metadata": {},
   "source": [
    "$T_1$ 以及布居数随闲置时间变化的图像如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5aab7bba",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Print estimated T1\n",
    "print(\"Estimated T1: \", T1, \"ns\")\n",
    "\n",
    "# Plot fit result\n",
    "plt.plot(tList, experimental, \"+\", label=\"Experiment\")\n",
    "plt.plot(tList, fitted, \"r\", label=\"Fitted\", linewidth=2.)\n",
    "plt.legend()\n",
    "plt.xlabel(\"Idling time\", size=15)\n",
    "plt.ylabel(r'$|1\\rangle$ population', size=15)\n",
    "plt.title(\"Longitudinal Relaxation\", size=17)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4a36b07b",
   "metadata": {},
   "source": [
    "## Ramsey 振荡标定 $T_2$\n",
    "\n",
    "在本节中，我们将使用 Ramsey 振荡实验进行失相时间 $T_2$ 的标定。首先，我们在量子比特上输入一个与量子比特频率相差非常小的驱动频率的 $\\pi/2$ 脉冲，在等待闲置时间 $t_{\\rm idle}$ 之后，再输入另一个 $\\pi/2$ 脉冲，并测量量子比特的激发态布居数 \\[2\\]。此时，测量结果取决于闲置时间 $t_{\\rm idle}$ 之后量子态的相位。\n",
    "\n",
    "为进行 Ramsey 实验，我们从 `Quanlse.Calibration.SingleQubit` 模块导入函数 `Ramsey()`，输入参数：模拟器对象 `pulseModel`、脉冲频率 `pulseFreq` 、$\\pi/2$ 脉冲持续时间 `tg` 、$\\pi/2$ 脉冲幅度 `x90` 、采样数 `sample` 、最大闲置时间 `maxTime` 和脉冲频率与比特频率的失调 `detuning`（该程序运行时间可能会比较久，可以选择减少采样点以及减少运行时间，但是模拟的效果可能也随之下降）："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f8727a72",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Scan different idle time for Ramsey oscillation\n",
    "tList, popList = ramsey(pulseModel=model, pulseFreq=5.21 * 2 * pi, tg=20, x90=1.013,\n",
    "                        sample=50, maxTime=600, detuning=0.07)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a2c150e5",
   "metadata": {},
   "source": [
    "该函数返回闲置时间和相应的布居数列表。我们可以使用函数 `fitRamsey()` 来对数据进行拟合，输入参数 $T_1$ `t1`、布居数列表 `popList`、闲置时间列表 `tList` 以及失调 `detuning`，然后，使用函数 $y=\\frac{1}{2} \\cos（a\\cdot x）e^{-b\\cdot x}+0.5$ 拟合曲线。根据拟合结果，我们使用表达式 $T_2 = \\frac{1}{(b-\\frac{1}{2a})}$ 求得 $T_2$："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "02eb2801",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Fit Ramsey\n",
    "T2, fitted = fitRamsey(t1=2000, popList=popList, tList=tList, detuning=0.07)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "495d93a9",
   "metadata": {},
   "source": [
    "`fitRamsey()` 返回测得的 $T_2$ 值和拟合的布居数列表，$T_2$ 以及激发态布居数与闲置时间关系图如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8c60c76",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Print estimated T2\n",
    "print(\"Estimated T2: \", T2, \" ns\")\n",
    "\n",
    "# Plot fit result\n",
    "plt.plot(tList, popList, '.')\n",
    "plt.plot(tList, fitted)\n",
    "plt.plot(tList, list(exp(- (1 / 600 + 1 / (2 * 2000)) * array(tList)) * 0.5 + 0.5))\n",
    "plt.xlabel(\"Idling time (ns)\", size=15)\n",
    "plt.ylabel(r\"$|1\\rangle$ population\", size=15)\n",
    "plt.title(\"Ramsey Experiment\", size=17)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "08b464c0",
   "metadata": {},
   "source": [
    "## 总结\n",
    "\n",
    "本教程了介绍如何使用量脉进行单量子比特的频率、$\\pi$ 脉冲、弛豫时间 $T_1$以及失相时间 $T_2$ 的标定。在阅读此教程后，用户可以通过这个链接 [tutorial-single-qubit-calibration-cn.ipynb](https://github.com/baidu/Quanlse/blob/main/Tutorial/CN/tutorial-single-qubit-calibration-cn.ipynb) 跳转到此 Jupyter Notebook 文档对应的 GitHub 页面获取相关的代码。我们鼓励用户尝试不同于本教程的参数值以获得最佳结果。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "55ad9629",
   "metadata": {},
   "source": [
    "## 参考文献\n",
    "\n",
    "\\[1\\] [Krantz, Philip, et al. \"A quantum engineer's guide to superconducting qubits.\" *Applied Physics Reviews* 6.2 (2019): 021318.](https://doi.org/10.1063/1.5089550)\n",
    "\n",
    "\\[2\\] [Ramsey, Norman F. \"A molecular beam resonance method with separated oscillating fields.\" *Physical Review* 78.6 (1950): 695.](https://doi.org/10.1103/PhysRev.78.695)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}